package com.ft.config;

import com.ft.model.enums.OrderStatusChangeEventEnum;
import com.ft.model.enums.OrderStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;

import java.util.EnumSet;

/**
 * 使用状态机工厂模式来处理多状态机情况
 * 为了提高性能，禁用spring应用程序上下文事件：contextEvents = false, 从而使用StateMachineListener接口
 * @author: zhangjianghua
 * @Date: 2018-10-09 17:02
 */
@Slf4j
@Configuration
@EnableStateMachineFactory(contextEvents = false)
public class OrderStateMachineConfiguration extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum> {
    /**订单状态机ID*/
    public static final String orderStateMachine = "orderStateMachine";

    /**
     * 配置状态机监听器
     * @param config
     * @throws Exception
     */
    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> config) throws Exception {
        config
                .withConfiguration()
                .autoStartup(false)
                .listener(listener());
    }

    /**
     * 配置状态
     * @param states
     * @throws Exception
     */
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> states) throws Exception {
        states
                .withStates()
                .initial(OrderStatusEnum.WAIT_FOR_PAYMENT)
                .states(EnumSet.allOf(OrderStatusEnum.class))
                .end(OrderStatusEnum.FULFILLED)
                .end(OrderStatusEnum.CANCELED);
    }
    /**
     * 配置状态转换事件关系
     * @param transitions
     * @throws Exception
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> transitions) throws Exception {
        transitions
                .withExternal().source(OrderStatusEnum.WAIT_FOR_PAYMENT).target(OrderStatusEnum.WAIT_FOR_DELIVER).event(OrderStatusChangeEventEnum.PAY)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_FOR_DELIVER).target(OrderStatusEnum.WAIT_FOR_RECEIVE).event(OrderStatusChangeEventEnum.DELIVERY)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_FOR_RECEIVE).target(OrderStatusEnum.FULFILLED).event(OrderStatusChangeEventEnum.RECEIVE)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_FOR_PAYMENT).target(OrderStatusEnum.CANCELED).event(OrderStatusChangeEventEnum.CANCEL);

    }

    private StateMachineListener<OrderStatusEnum, OrderStatusChangeEventEnum> listener() {
        return new StateMachineListenerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum>() {
            @Override
            public void stateChanged(State from, State to) {
                log.info("State changed from {} to {}",from.getId(), to.getId());
            }
            @Override
            public void eventNotAccepted(Message<OrderStatusChangeEventEnum> event) {
                log.error("Event not accepted: {}", event.getPayload());
            }
        };
    }
}
